#!/bin/sh

# <browser name>:<chroot binary path>
browser_list=$(cat << END
chrome:/opt/google/chrome/chrome
vivaldi:/opt/vivaldi/vivaldi
brave:/opt/brave.com/brave/brave
opera:/usr/lib/x86_64-linux-gnu/opera/opera
edge:/opt/microsoft/msedge/microsoft-edge
END
)

chroot_mount_points="
/proc
/sys
/dev
/dev/fd
/dev/shm
/tmp
"

prefix=/usr/local
chroot_path=/compat/ubuntu
ubuntu_version=focal

my_dir="$(dirname $(realpath $0))"
bindir="${prefix}/bin"
appsdir="${prefix}/share/applications"
chroot_bindir="${chroot_path}/bin"

apt_packages="gnupg pulseaudio ca-certificates apt-transport-https curl"
pkg_list="debootstrap pulseaudio"

export DEBIAN_FRONTEND=noninteractive
export APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1

bail()
{
	if [ $# -gt 0 ]; then
		echo "${0##*/}: Error: $*" >&2
	fi
	exit 1
}

mk_mount_dirs()
{
	local dir p
	for p in ${chroot_mount_points}; do
		dir="${chroot_path}/$p"
		[ ! -d "${dir}" ] && mkdir -p "${dir}"
	done
}

umount_chroot()
{
	local mntpts _chroot_path p _p

	_chroot_path=$(realpath "${chroot_path}")
	[ $? -ne 0 -o -z "${_chroot_path}" ] && exit 1
	mntpts=$(mount -p | awk -F'[ \t]+' -v chroot=${_chroot_path} '
		$2 ~ sprintf("^%s/", chroot) {
			mp[n++] = $2
		}
		END {
			while (--n >= 0) print mp[n]
		}
	')
	for p in ${mntpts}; do
		_p=$(realpath "$p")
		[ $? -ne 0 -o -z "${_p}" ] && exit 1
		umount "${_p}" || exit 1
		if (mount -p | grep -q "${_p}/"); then
			bail "Couldn't unmount ${_p}"
		fi
	done
}

install_rc()
{
	local tmpl="${my_dir}/rc-ubuntu.tmpl"
	local target="${prefix}/etc/rc.d/ubuntu"
	sed -E "s#@CHROOT_PATH@#${chroot_path}#g" < ${tmpl} > ${target}
	chmod 555 ${target}
}

install_packages()
{
	for p in ${pkg_list}; do
		pkg info --exists $p && continue
		pkg install -y $p || bail "'pkg install -y $p' failed"
	done
}

fix_ld_path()
{
	local lib

	for lib in ${chroot_path}/lib/x86_64-linux-gnu/ld-*; do
		if [ ! -L ${lib} ]; then
			[ -L ${chroot_path}/lib64/ld-linux-x86-64.so.2 ] && \
				unlink ${chroot_path}/lib64/ld-linux-x86-64.so.2
			cp ${lib} ${chroot_path}/lib64/ld-linux-x86-64.so.2
			return
		fi
	done
}

do_chroot()
{
	local ignore_errors=0

	while [ $# -gt 0 ]; do
		case "$1" in
		-i) ignore_errors=1
			;;
		-*) bail "do_chroot: Invalid flag '$1'"
			;;
		 *) break
			;;
		esac
		shift
	done
	chroot ${chroot_path} /bin/bash -c "$*" && return 0
	[ ${ignore_errors} -eq 0 ] && bail "Command '$*' failed"
}

install_apt_packages()
{
	do_chroot -i 'apt update'
	do_chroot -i 'apt remove -y rsyslog'
	for p in ${apt_packages}; do
		do_chroot "apt install -y $p"
	done
}

install_starter()
{
	local browser_name=$1
	local tmpl="${my_dir}/starter.tmpl"
	local target="${bindir}/linux-${browser_name}"

	sed -E "s#@CHROOT_PATH@#${chroot_path}#g;s#@BROWSER@#${browser_name}#g;" \
		< ${tmpl} > ${target}
	chmod 555 ${target}
}

install_chroot_wrapper()
{
	local browser_name=$1
	local binary_path=$(get_chroot_binary_path ${browser_name})
	local binary_name=$(basename ${binary_path})
	local binary_dir=$(dirname ${binary_path})
	local target=${chroot_bindir}/${browser_name}
	[ -L ${target} ] && unlink -f ${target}
	sed -E "s|@BROWSER@|${binary_name}|g;s|@BROWSER_PATH@|${binary_dir}|g" \
	  < ${my_dir}/chroot-wrapper.tmpl > ${target}
	chmod 555 ${target}
}

create_dir()
{
	local dir_name=$1
	[ ! -d "${dir_name}" ] && mkdir -p "${dir_name}"
}

remove_empty_dirs()
{
	for d in ${prefix}/share/fonts; do
		[ -d "${chroot_path}/$d" ] && \
			rmdir "${chroot_path}/$d"
	done
}

get_chroot_binary_path()
{
	local key=$1
	echo ${browser_list} | tr "${IFS}" '\n' | grep "${key}:" | cut -d: -f2
}

check_installed()
{
	local browser_name=$1
	local path=$(get_chroot_binary_path ${browser_name})
	[ -x ${chroot_path}/${path} ]
}

install_browser_files()
{
	local browser_name=$1

	for d in "${chroot_bindir}" "${bindir}" "${appsdir}"; do
		create_dir "$d"
	done
	install_chroot_wrapper ${browser_name}
	install_starter ${browser_name}
	install -m 0644 ${my_dir}/linux-${browser_name}.desktop "${appsdir}"
}

deinstall_browser_files()
{
	local browser_name=$1
	rm -f "${appsdir}/linux-${browser_name}.desktop"
	rm -f "${bindir}/linux-${browser_name}"
	rm -f "${chroot_bindir}/${browser_name}"
}

install_chrome()
{
	echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ " \
	     "stable main" > \
		"${chroot_path}/etc/apt/sources.list.d/google-chrome.list"
	fetch -o ${chroot_path} \
		https://dl.google.com/linux/linux_signing_key.pub || exit 1
	do_chroot "apt-key add linux_signing_key.pub"
	do_chroot -i "apt update"
	do_chroot "apt install -y google-chrome-stable"
}

deinstall_chrome()
{
	do_chroot "apt remove -y google-chrome-stable"
}

install_brave()
{
	do_chroot \
		'curl -s https://brave-browser-apt-release.s3.brave.com/brave-core.asc | apt-key --keyring /etc/apt/trusted.gpg.d/brave-browser-release.gpg add -'
	do_chroot \
		'echo "deb [arch=amd64] https://brave-browser-apt-release.s3.brave.com/ stable main" > /etc/apt/sources.list.d/brave-browser-release.list'
	do_chroot -i "apt update"
	do_chroot "apt install -y brave-browser"
}

deinstall_brave()
{
	do_chroot "apt remove -y brave-browser"
}

install_vivaldi()
{
	echo "deb [arch=amd64] https://repo.vivaldi.com/archive/deb/ " \
	     "stable main" > \
		"${chroot_path}/etc/apt/sources.list.d/vivaldi.list"
	fetch -o ${chroot_path} \
		https://repo.vivaldi.com/archive/linux_signing_key.pub || exit 1
	do_chroot "apt-key add linux_signing_key.pub"
	do_chroot -i "apt update"
	do_chroot "apt install -y vivaldi-stable"
}

deinstall_vivaldi()
{
	do_chroot "apt remove -y vivaldi-stable"
}

install_edge()
{
	do_chroot \
		'curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | apt-key --keyring /etc/apt/trusted.gpg.d/microsoft.gpg add -'
	do_chroot \
		'echo "deb [arch=amd64] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-stable.list'
	do_chroot -i "apt update"
	do_chroot "apt install -y microsoft-edge-stable"
}

deinstall_edge()
{
	do_chroot "apt remove -y microsoft-edge-stable"
}

install_opera()
{
	do_chroot 'curl https://deb.opera.com/archive.key | apt-key add -'
	do_chroot \
		'echo "deb https://deb.opera.com/opera-stable/ stable non-free" > /etc/apt/sources.list.d/opera-stable.list'
	do_chroot -i "apt update"
	do_chroot "apt install -y opera-stable"
}

deinstall_opera()
{
	do_chroot "apt remove -y opera-stable"
}

symlink_icons()
{
	local name i
	[ ! -d ${chroot_path}/usr/share/icons ] && \
		mkdir -p ${chroot_path}/usr/share/icons
	for i in ${prefix}/share/icons/*; do
		[ ! -d $i ] && continue
		name=$(basename $i)
		[ -e ${chroot_path}/usr/share/icons/${name} ] && continue
		ln -s $i ${chroot_path}/usr/share/icons
	done
}

symlink_themes()
{
	local name i
	[ ! -d ${chroot_path}/usr/share/themes ] && \
		mkdir -p ${chroot_path}/usr/share/themes
	for i in ${prefix}/share/themes/*; do
		[ ! -d $i ] && continue
		name=$(basename $i)
		[ -e ${chroot_path}/usr/share/themes/${name} ] && continue
		ln -s $i ${chroot_path}/usr/share/themes
	done
}

set_timezone()
{
	printf "0.0 0 0.0\n0\nUTC\n" > ${chroot_path}/etc/adjtime
	if [ ! -d "${chroot_path}/etc/localtime" ]; then
		mkdir -p "${chroot_path}/etc/localtime"
	fi
	cp /var/db/zoneinfo "${chroot_path}/etc/timezone" \
		${chroot_path}/etc/localtime
	rm -f ${chroot_path}/etc/localtime
	ln -s "/usr/share/zoneinfo/$(cat /var/db/zoneinfo)" \
		${chroot_path}/etc/localtime
	do_chroot -i "dpkg-reconfigure --frontend noninteractive tzdata"
}

install_chroot_base()
{
	[ -f ${chroot_path}/etc/os-release ] && return
	mk_mount_dirs
	install_rc
	sysrc linux_enable=NO
	sysrc ubuntu_enable=YES
	service ubuntu start || bail "Failed to start ubuntu service"
	install_packages
	debootstrap --arch=amd64 --no-check-gpg ${ubuntu_version} ${chroot_path} || \
		bail "debootstrap failed"
	echo "APT::Cache-Start 251658240;" > \
		${chroot_path}/etc/apt/apt.conf.d/00aptitude

	:>${chroot_path}/etc/apt/sources.list
	for x in "" "-updates" "-security"; do
		echo "deb http://archive.ubuntu.com/ubuntu/ ${ubuntu_version}$x " \
	    	 "main restricted universe multiverse" >> \
			${chroot_path}/etc/apt/sources.list
	done
	fix_ld_path
	set_timezone
	service ubuntu start
	install_apt_packages
	symlink_icons
	symlink_themes
	# Accessing the dbus socket doesn't work if /var/run is a symlink
	[ -L ${chroot_path}/var/run ] && unlink ${chroot_path}/var/run
}

deinstall_chroot_base()
{
	local path
	path=$(realpath ${chroot_path})
	[ $? -ne 0 ] && exit 1

	if [ "${path}" = "/" ]; then
		echo "chroot_path must not be '/'" >&2
		exit 1
	fi
	umount_chroot
	rm -rf "${path}"
	rm -f "${prefix}/etc/rc.d/ubuntu"
	sysrc -x ubuntu_enable
}

upgrade_chroot()
{
	local flags="-q -y --allow-downgrades"
	flags="${flags} --allow-remove-essential --allow-change-held-packages"
	do_chroot "apt-get update && apt upgrade ${flags}"
	fix_ld_path
}

install_browser()
{
	local browser_name=$1

	if check_installed ${browser_name}; then
		echo "${browser_name} already installed" >&2
		exit 1
	fi
	install_chroot_base
	eval install_${browser_name}
	install_browser_files ${browser_name}
	remove_empty_dirs
}

deinstall_browser()
{
	local browser_name=$1
	eval deinstall_${browser_name}
	deinstall_browser_files ${browser_name}
}

usage()
{
	echo "Usage: $0 install <brave|chrome|edge|vivaldi|opera>"
	echo "       $0 deinstall <brave|chrome|edge|vivaldi|opera>"
	echo "       $0 chroot <create|upgrade|delete>"
	echo "       $0 symlink <icons|themes>"
	exit 1
}


if [ $(id -u) -ne 0 ]; then
	echo "This script must be run as root" 1>&2
	exit 1
fi

[ $# -eq 0 ] && usage

while [ $# -gt 0 ]; do
	case "$1" in
	install)
		case $2 in
		brave|chrome|edge|vivaldi|opera)
			browser=$2
			install_browser ${browser}
			exit 0
			;;
		*)
			usage
			;;
		esac
		shift
		;;
	deinstall)
		case $2 in
		brave|chrome|edge|vivaldi|opera)
			browser=$2
			deinstall_browser ${browser}
			exit 0
			;;
		*)
			usage
			;;
		esac
		shift
		;;
	chroot|jail)
		case $2 in
		create)
			install_chroot_base
			exit 0
			;;
		delete)
			deinstall_chroot_base
			exit 0
			;;
		upgrade)
			upgrade_chroot
			exit 0
			;;
		*)
			usage
			;;
		esac
		shift
		;;
	symlink)
		case $2 in
		icons|themes)
			eval symlink_$2
			exit 0
			;;
		*)
			usage
			;;
		esac
		shift
		;;
	*)
		usage
		;;
	esac
	shift
done

